package org.karelun.gef.parts;

import java.beans.PropertyChangeEvent;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.RGB;
import org.karelun.gef.figure.ClassFigure;
import org.karelun.gef.model.AttributeModel;
import org.karelun.gef.model.ClassModel;
import org.karelun.gef.model.MethodModel;
import org.karelun.gef.model.NodeConstants;
import org.karelun.gef.policies.ClassLayoutEditPolicy;
import org.karelun.gef.utils.FontDataUtils;

public class ClassPart extends NodePart {

	private List<AttributePart> attrParts = null;

	private List<MethodPart> methodParts = null;

	private BaseAttrPart currentChildPart = null;

	public IFigure getContentPane() {
		ClassFigure classFigure = (ClassFigure) getFigure();
		if (currentChildPart != null
				&& (currentChildPart instanceof MethodPart))
			return classFigure.getMethodFigure();
		else
			return classFigure.getAttributeFigure();
	}

	@Override
	protected void addChildVisual(EditPart childEditPart, int index) {
		currentChildPart = (BaseAttrPart) childEditPart;
		if (childEditPart instanceof AttributePart) {
			addAttrParts((AttributePart) childEditPart);
		} else if (childEditPart instanceof MethodPart) {
			addMethodParts((MethodPart) childEditPart);
		}
		index = getChildPartIndex(childEditPart, index);
		super.addChildVisual(childEditPart, index);
	}

	@Override
	protected void removeChildVisual(EditPart childEditPart) {
		currentChildPart = (BaseAttrPart) childEditPart;
		if (childEditPart instanceof AttributePart)
			removeAttrParts((AttributePart) childEditPart);
		else if (childEditPart instanceof MethodPart)
			removeMethodParts((MethodPart) childEditPart);
		super.removeChildVisual(childEditPart);
	}

	private void removeAttrParts(AttributePart attrPart) {
		if (attrParts == null)
			return;
		attrParts.remove(attrPart);

	}

	private void removeMethodParts(MethodPart methodPart) {
		if (methodParts == null)
			return;
		methodParts.remove(methodPart);
	}

	private void addAttrParts(AttributePart attrPart) {
		if (attrParts == null)
			attrParts = new ArrayList<AttributePart>();
		attrParts.add(attrPart);
	}

	private void addMethodParts(MethodPart methodPart) {
		if (methodParts == null)
			methodParts = new ArrayList<MethodPart>();
		methodParts.add(methodPart);
	}

	private int getChildPartIndex(EditPart childEditPart, int index) {
		if (childEditPart instanceof AttributePart) {
			AttributePart attrPart = (AttributePart) childEditPart;
			return attrParts.indexOf(attrPart);
		} else if (childEditPart instanceof MethodPart) {
			MethodPart methodPart = (MethodPart) childEditPart;
			return methodParts.indexOf(methodPart);
		}
		return index;
	}

	protected IFigure createFigure() {
		ClassFigure classFigure = new ClassFigure();
		return classFigure;
	}

	protected void createEditPolicies() {
		super.createEditPolicies();
		installEditPolicy(EditPolicy.LAYOUT_ROLE, new ClassLayoutEditPolicy());
	}

	@Override
	protected void refreshVisuals() {
		ClassModel model = (ClassModel) getModel();
		ClassFigure figure = (ClassFigure) getFigure();

		Point loc = model.getLocation();
		FontData data = model.getFontData();
		Dimension fontSize = FontDataUtils.getFontSize(model.getText(), data);
		Dimension attrSize = getChildrenSize(model);
		Dimension newSize = calculate(fontSize, attrSize);
		Rectangle rectangle = new Rectangle(loc, newSize);
		EditPart parent = getParent();
		((DiagramPart) parent).setLayoutConstraint(this, figure, rectangle);

		figure.setText(model.getText());

		RGB foreColor = model.getForeColor();
		if (foreColor != null)
			figure.setForeColor(foreColor);
		RGB bgColor = model.getBgColor();
		if (bgColor != null)
			figure.setBGColor(bgColor);

		super.refreshVisuals();
	}

	private Dimension calculate(Dimension fontSize, Dimension attrSize) {
		if (fontSize == null)
			return attrSize;
		int fontWidth = fontSize.width;
		int modelSize = attrSize.width;
		return fontWidth > modelSize ? fontSize : attrSize;
	}

	private Dimension getChildrenSize(ClassModel model) {
		Dimension size = model.getSize();
		List<AttributeModel> attrModels = model.getAttrModels();
		List<MethodModel> methodModels = model.getMethodModels();
		if (attrModels.isEmpty() && methodModels.isEmpty())
			return size;
		int heigh = 20;
		int width = 0;
		if (!attrModels.isEmpty()) {
			AttributeModel attrModel = attrModels.get(0);
			int h = attrModel.getSize().height;
			heigh += h * attrModels.size();
			width = getMaxWidthAttribute(attrModels);
		}
		if (!methodModels.isEmpty()) {
			MethodModel methodModel = methodModels.get(0);
			int h = methodModel.getSize().height;
			heigh += h * methodModels.size();
			int w=getMaxWidthMethod(methodModels);
			width=w > width ? w : width;
		}
		heigh += 5;
		if (heigh > size.height)
			size.height = heigh;
		if(width>size.width)
			size.width=width;
		return size;
	}

	private int getMaxWidthMethod(List<MethodModel> methodModels) {
		int width=0;
		for(MethodModel model:methodModels){
			int w=model.getSize().width;
			if(w>width)
				width=w;
		}
		return width;
	}

	private int getMaxWidthAttribute(List<AttributeModel> attrModels) {
		int width=0;
		for(AttributeModel model:attrModels){
			int w=model.getSize().width;
			if(w>width)
				width=w;
		}
		return width;
	}

	@SuppressWarnings("unchecked")
	protected List getModelChildren() {
		List<Object> list = new ArrayList<Object>();
		ClassModel classModel = getClassModel();
		List<AttributeModel> attrModels = classModel.getAttrModels();
		list.addAll(attrModels);

		List<MethodModel> methodModels = classModel.getMethodModels();
		list.addAll(methodModels);
		return list;
	}

	public void activate() {
		super.activate();
		getClassModel().addPropertyChangeListener(this);
	}

	public void deactivate() {
		getClassModel().removePropertyChangeListener(this);
		super.deactivate();
	}

	public void propertyChange(PropertyChangeEvent evt) {
		if (evt.getPropertyName().equals(NodeConstants.PROP_TARGET))
			refreshTargetConnections();
		else if (evt.getPropertyName().equals(NodeConstants.PROP_SOURCE))
			refreshSourceConnections();
		else if (NodeConstants.PROP_LOCATION.equals(evt.getPropertyName())
				|| NodeConstants.PROP_SIZE.equals(evt.getPropertyName()))
			refreshVisuals();

		if (ClassModel.PROP_ATTRIBUTE.equals(evt.getPropertyName()))
			refresh();
		if (ClassModel.PROP_METHOD.equals(evt.getPropertyName()))
			refresh();
		else if (NodeConstants.PROP_TEXT.equals(evt.getPropertyName())) {
			refreshVisuals();
		} else if (NodeConstants.PROP_FORE_COLOR.equals(evt.getPropertyName()))
			refreshVisuals();
		else if (NodeConstants.PROP_BG_CLOR.equals(evt.getPropertyName()))
			refreshVisuals();
		super.propertyChange(evt);
	}

	protected ClassModel getClassModel() {
		return (ClassModel) getModel();
	}
}